#include <boost/foreach.hpp>

#include "InterpreterVisitor.h"
#include "Exceptions.h"

using namespace boost;

InterpreterVisitor::InterpreterVisitor(FunctionDefinition* iEntryPoint)
{
    fEntryPoint = iEntryPoint;
}

InterpreterVisitor::~InterpreterVisitor()
{
    while(!fLocalVars.empty())
    {
        typedef map<string, ExpressionValue*> MapType;
        MapType lVars = fLocalVars.top();

        BOOST_FOREACH(MapType::value_type& i, lVars)
        {
            this->DereferenceValue(i.second);
        }
        /*for(map<string, ExpressionValue*>::iterator it = lVars.begin(); it != lVars.end(); ++it)
        {
            this->DereferenceValue((*it).second);
        }*/

        lVars.clear();
        fLocalVars.pop();
    }

    while(!fExecutionResults.empty())
    {
        ExecutionResult* lResult = fExecutionResults.top();

        if(lResult->fResult != 0)
            this->DereferenceValue(lResult->fResult);

        delete lResult;

        fExecutionResults.pop();
    }

    while(!fCallParamsStack.empty())
    {
        vector<ExpressionValue*> lCallParams = fCallParamsStack.top();

        BOOST_FOREACH(ExpressionValue * i, lCallParams)
        {
            this->DereferenceValue(i);
        }
        /*for(vector<ExpressionValue*>::iterator it = lCallParams.begin(); it != lCallParams.end(); ++it)
        {
            this->DereferenceValue(*it);
        }*/

        lCallParams.clear();
        fCallParamsStack.pop();
    }

    while(!fExpressionStack.empty())
    {
        this->DereferenceValue(fExpressionStack.top());
        fExpressionStack.pop();
    }

    for(vector<ExpressionValue*>::iterator it = fHeap.begin(); it != fHeap.end(); ++it)
    {
        this->DereferenceValue(*it);
    }

    fHeap.clear();
}

void InterpreterVisitor::Run()
{
    if(this->fEntryPoint == 0)
    {
        throw MainFunctionNotDefinedException();
    }

    // pokud bude funkce main prijimat nejake argumenty, tady ji je predat
    vector<ExpressionValue*> lMainParams;
    this->fCallParamsStack.push(lMainParams);

    this->fEntryPoint->Accept(this);
}

void InterpreterVisitor::DereferenceValue(ExpressionValue* oValue)
{
    oValue->Dereference();

    if(!oValue->IsReferenced())
    {
        delete oValue;
        oValue = 0;
    }
}

void InterpreterVisitor::SetLocalVar(string iVarName, ExpressionValue* iValue)
{
    map<string, ExpressionValue*>& lVars = fLocalVars.top();

    if(lVars.find(iVarName) == lVars.end())
    {
        lVars[iVarName] = iValue;
        this->ReferenceValue(iValue);
    }
    else
    {
        this->DereferenceValue(lVars[iVarName]);
        lVars[iVarName] = iValue;
        this->ReferenceValue(iValue);
    }
}

void InterpreterVisitor::Visit(ArrayAccess* iNode)
{
}

void InterpreterVisitor::Visit(BinaryOperation* iNode)
{
}

void InterpreterVisitor::Visit(ConstantValue* iNode)
{
}

void InterpreterVisitor::Visit(FieldAccess* iNode)
{
}

void InterpreterVisitor::Visit(UnaryOperation* iNode)
{
}

void InterpreterVisitor::Visit(Variable* iNode)
{
}

void InterpreterVisitor::Visit(Assign* iNode)
{
}

void InterpreterVisitor::Visit(EmptyStatement* iNode)
{
}

void InterpreterVisitor::Visit(ForLoop* iNode)
{
}

void InterpreterVisitor::Visit(IfStatement* iNode)
{
}

void InterpreterVisitor::Visit(NewExpression* iNode)
{
}

void InterpreterVisitor::Visit(ReturnStatement* iNode)
{
}

void InterpreterVisitor::Visit(StatementList* iNode)
{
}

void InterpreterVisitor::Visit(VarDeclaration* iNode)
{
}

void InterpreterVisitor::Visit(WhileLoop* iNode)
{
}

void InterpreterVisitor::Visit(FunctionCall* iNode)
{
}

void InterpreterVisitor::Visit(MethodCall* iNode)
{
}

void InterpreterVisitor::Visit(ClassDefinition* iNode)
{
}

void InterpreterVisitor::Visit(FunctionDefinition* iNode)
{
    // tady se bude delat volani funkce

    vector<ExpressionValue*> lParameters = this->fCallParamsStack.top();

    int lRqCount = iNode->GetParamCount();
    int lActualCount = lParameters.size();

    if(lParameters.size() != iNode->GetParamCount())
    {
        throw FunctionCallWithWrongParamCountException(iNode->GetName(), lRqCount, lActualCount);
    }

    // zaloz mapu pro lokalni promenne a dej to na zasobnik...
    map<string, ExpressionValue*> lLocalVarsBase;
    this->fLocalVars.push(lLocalVarsBase);

    vector<Variable*> lInputVars = iNode->GetInputVars();

    // hodnoty parametru netreba kopirovat - to uz se pripadne vyresilo pri jejich vlozeni na zasobnik
    // tj. pro hodnotove typy (cisla, booly, stringy, ...) se vytvorila kopie hodnoty, pro referencni (pole, objekty)
    // se tam dal odkaz na hodnotu (tj. reference)
    for(int i = 0; i < lRqCount; i++)
    {
        this->SetLocalVar(lInputVars[i]->GetVarName(), lParameters[i]);
    }

    // TODO tady vykonej telo

    // tady po sobe vycisti parametry
    for(vector<ExpressionValue*>::iterator it = lParameters.begin(); it != lParameters.end(); ++it)
    {
        this->DereferenceValue(*it);
    }

    this->fCallParamsStack.pop();

    // TODO do�e�it vracen� hodnoty
}

void InterpreterVisitor::Visit(MethodDefinition* iNode)
{
}
